﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using NextCore;
namespace NextDraw
{
    public class SRectTex : Shader
    {
        public SRectTex()
            : base("sd/vrect.glsl", "sd/frecttex.glsl")
        {
        }
    }
    public class SRect : Shader
    {
        public SRect() : base("sd/vrect.glsl","sd/frect.glsl")
        {

        }
    }
    public class BufferGenError : Exception
    {
        public BufferGenError()
            : base("Unable to generate buffers, cannot continue.")
        {
        }
    }
    public static class Draw
    {
        public static int W, H;
        public static SRect RectS;
        public static SRectTex RectTexS;
        public static Matrix4 DrawMat4;
        public static int IB, IV;
        public static float[] DV = new float[255];
        public static short[] DI = new short[255];
        public static void InitDraw()
        {
            RectTexS = new SRectTex();
            RectS = new SRect();
            IB = GL.GenBuffer();
            IV = GL.GenBuffer();
            if (IB == 0 || IV == 0)
            {
                throw new BufferGenError();
            }
            
        }
        public static void Rect(int x, int y, int w, int h, Tex2D tex)
        {
            GL.Enable(EnableCap.Texture2D);
            GL.BindTexture(TextureTarget.Texture2D, tex.ID);

            DV[0] = x;
            DV[1] = y;
            DV[2] = 0;
            DV[3] = 0;

            DV[4] = x + w;
            DV[5] = y;
            DV[6] = 1;
            DV[7] = 0;

            DV[8] = x + w;
            DV[9] = y + h;
            DV[10] = 1;
            DV[11] = 1;

            DV[12] = x;
            DV[13] = y + h;
            DV[14] = 0;
            DV[15] = 1;

            DI[0] = 0;
            DI[1] = 1;
            DI[2] = 2;

            DI[3] = 2;
            DI[4] = 3;
            DI[5] = 0;

            InitBuf(RectTexS);
            RectTexS.SetMat4("m_World", DrawMat4);
            RectTexS.Use();
            DrawBuf();
            RectTexS.Release();
            
            GL.Disable(EnableCap.Texture2D);
        }
        public static void Rect(int x, int y, int w, int h)
        {
            DV[0] = x;
            DV[1] = y;
            DV[2] = 0;
            DV[3] = 0;

            DV[4] = x+w;
            DV[5] = y;
            DV[6] = 1;
            DV[7] = 0;

            DV[8] = x+w;
            DV[9] = y+h;
            DV[10] = 1;
            DV[11] = 1;

            DV[12] = x;
            DV[13] = y+h;
            DV[14] = 0;
            DV[15] = 1;

            DI[0] = 0;
            DI[1] = 1;
            DI[2] = 2;
            
            DI[3] = 2;
            DI[4] = 3;
            DI[5] = 0;

            InitBuf(RectS);
            RectS.SetMat4("m_World", DrawMat4);
            RectS.Use();
            DrawBuf();
            RectS.Release();
         
           
        }
        public static void Rect3D(int x, int y, int w, int h, Vector3 rot, Tex2D tex)
        {
            //GL.Enable(EnableCap.Texture2D);
            // GL.BindTexture(TextureTarget.Texture2D, tex.ID);

            DV[0] = x;
            DV[1] = y;
            DV[2] = 0;
            DV[3] = 0;

            DV[4] = x + w;
            DV[5] = y;
            DV[6] = 1;
            DV[7] = 0;

            DV[8] = x + w;
            DV[9] = y + h;
            DV[10] = 1;
            DV[11] = 1;

            DV[12] = x;
            DV[13] = y + h;
            DV[14] = 0;
            DV[15] = 1;

            DI[0] = 0;
            DI[1] = 1;
            DI[2] = 2;

            DI[3] = 2;
            DI[4] = 3;
            DI[5] = 0;

            rot = ConvertRot(rot);

            Matrix4 xr = Matrix4.CreateRotationX(rot.X);
            Matrix4 yr = Matrix4.CreateRotationY(rot.Y);
            Matrix4 rr = Matrix4.CreateRotationZ(rot.Z);

            Matrix4 sum = xr * yr;
            sum = rr * sum;

            Vector3 p1;
            Vector3 p2;
            Vector3 p3;
            Vector3 p4;
            sum = Transform(w, h, sum, out p1, out p2, out p3, out p4);

            float zs = 0.002f;

            RescaleAll(ref p1, ref p2, ref p3, ref p4, zs);
            ScaleAll(ref p1, ref p2, ref p3, ref p4);



            p1 = Repos(x, y, w, h, p1);
            p2 = Repos(x, y, w, h, p2);
            p3 = Repos(x, y, w, h, p3);
            p4 = Repos(x, y, w, h, p4);

            ApplyCoords(ref p1, ref p2, ref p3, ref p4);

            float topRightX = p2.X;
            float topRightY = p2.Y;
            float topLeftX = p1.X;
            float topLeftY = p1.Y;
            float bottomLeftX = p4.X;
            float bottomRightX = p3.X;
            float bottomLeftY = p4.Y;
            float bottomRightY = p3.Y;

            float[] ad = new float[20];
            float ax = topRightX - bottomLeftX;
            float ay = topRightY - bottomLeftY;
            float bx = topLeftX - bottomRightX;
            float by = topLeftY - bottomRightY;

            float cross = ax * by - ay * bx;

            if (cross != 0)
            {
                float cy = bottomLeftY - bottomRightY;
                float cx = bottomLeftX - bottomRightX;

                float s = (ax * cy - ay * cx) / cross;

                if (s > 0 && s < 1)
                {
                    float t = (bx * cy - by * cx) / cross;

                    if (t > 0 && t < 1)
                    {

                        float u0 = 0;
                        float v0 = 0;
                        float u2 = 1;
                        float v2 = 1;


                        float q0 = 1 / (1 - t);
                        float q1 = 1 / (1 - s);
                        float q2 = 1 / t;
                        float q3 = 1 / s;

                        ad[0] = p1.X;
                        ad[1] = p1.Y;
                        ad[2] = u0 * q0;
                        ad[3] = v2 * q0;
                        ad[4] = q0;

                        ad[5] = p2.X;
                        ad[6] = p2.Y;
                        ad[7] = u2 * q1;
                        ad[8] = v2 * q1;
                        ad[9] = q1;



                        ad[10] = p3.X;
                        ad[11] = p3.Y;
                        ad[12] = u2 * q2;
                        ad[13] = v0 * q2;
                        ad[14] = q2;

                        ad[15] = p4.X;
                        ad[16] = p4.Y;
                        ad[17] = u0 * q3;
                        ad[18] = v0 * q3;
                        ad[19] = q3;






                    }
                }
            }

            InitBuf(RectTexS,20*4,ad);
            RectTexS.SetMat4("m_World", DrawMat4);
            RectTexS.Use();
            DrawBuf();
            RectTexS.Release();

            GL.Disable(EnableCap.Texture2D);
        }

        private static void DrawBuf()
        {

            GL.DrawElements(BeginMode.Triangles, 6, DrawElementsType.UnsignedShort, 0);
        }
        private static void RescaleAll(ref Vector3 p1, ref Vector3 p2, ref Vector3 p3, ref Vector3 p4, float zs)
        {
            p1 = RescaleZ(p1, zs);
            p2 = RescaleZ(p2, zs);
            p3 = RescaleZ(p3, zs);
            p4 = RescaleZ(p4, zs);
        }

        private static void ApplyCoords(ref Vector3 p1, ref Vector3 p2, ref Vector3 p3, ref Vector3 p4)
        {
            return;
            DV[0] = p1.X;
            DV[1] = p1.Y;

            DV[5] = p2.X;
            DV[6] = p2.Y;

            DV[8] = p3.X;
            DV[9] = p3.Y;

            DV[12] = p4.X;
            DV[13] = p4.Y;
        }

        private static Matrix4 Transform(int w, int h, Matrix4 sum, out Vector3 p1, out Vector3 p2, out Vector3 p3, out Vector3 p4)
        {
            p1 = Vector3.TransformPosition(new Vector3(-w / 2, -h / 2, 0), sum);
            p2 = Vector3.TransformPosition(new Vector3(w / 2, -h / 2, 0), sum);
            p3 = Vector3.TransformPosition(new Vector3(w / 2, h / 2, 0), sum);
            p4 = Vector3.TransformPosition(new Vector3(-w / 2, h / 2, 0), sum);
            return sum;
        }

        private static Vector3 ConvertRot(Vector3 rot)
        {
            rot.X = (float)OpenTK.MathHelper.DegreesToRadians(rot.X);
            rot.Y = (float)OpenTK.MathHelper.DegreesToRadians(rot.Y);
            rot.Z = (float)OpenTK.MathHelper.DegreesToRadians(rot.Z);
            return rot;
        }

        private static Vector3 RescaleZ(Vector3 p1, float zs)
        {
            p1.Z *= zs;
            return p1;
        }

        private static Vector3 ScaleByZ(Vector3 p1)
        {
            p1.X += p1.X * p1.Z;
            p1.Y += p1.Y * p1.Z;
            return p1;
        }
        private static void ScaleAll(ref Vector3 p1, ref Vector3 p2, ref Vector3 p3, ref Vector3 p4)
        {
            p1 = ScaleByZ(p1);

            p2 = ScaleByZ(p2);

            p3 = ScaleByZ(p3);

            p4 = ScaleByZ(p4);
        }
        private static Vector3 Repos(int x, int y, int w, int h, Vector3 p1)
        {
            p1.X += x + w / 2;
            p1.Y += y + h / 2;
            return p1;
        }
        private static void InitBuf(Shader s,int bl=16*4,float[] a = null)
        {

            GL.EnableClientState(ArrayCap.VertexArray);
            GL.BindBuffer(BufferTarget.ArrayBuffer, IV);
            GL.BufferData(BufferTarget.ArrayBuffer, a==null ? new IntPtr(16 * 4) : new IntPtr(20*4), a==null ? DV : a, BufferUsageHint.StaticDraw);
            GL.Enable(EnableCap.VertexArray);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, IB);
            GL.BufferData(BufferTarget.ElementArrayBuffer, new IntPtr(6 * 2), DI, BufferUsageHint.StaticDraw);



            int va = s.GetAtt("vert");
            GL.EnableVertexAttribArray(va);
            GL.VertexAttribPointer(va, 2, VertexAttribPointerType.Float, false,a==null ? 16 : 20, 0);

            int ia = s.GetAtt(a==null ? "uv" : "uv");

            GL.EnableVertexAttribArray(ia);
            GL.VertexAttribPointer(ia,a==null ? 2 : 3, VertexAttribPointerType.Float, false, a==null ? 16 : 20, 8);

            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.MultMatrix(ref DrawMat4);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
            GL.Disable(EnableCap.Texture2D);
        }
    
    }
}
